Skip to content

feat(keeper): integrate filesystem backend with keeper trait - #583

Open
aldy505 wants to merge 4 commits into
aldy505/feat/ttl-keeperfrom
aldy505/feat/integrate-filesystem-keeper
Open

feat(keeper): integrate filesystem backend with keeper trait#583
aldy505 wants to merge 4 commits into
aldy505/feat/ttl-keeperfrom
aldy505/feat/integrate-filesystem-keeper

Conversation

@aldy505

@aldy505 aldy505 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@aldy505
aldy505 requested a review from a team as a code owner August 1, 2026 13:07
keeper: KeeperConfig {
backend: KeeperBackend::Sqlite,
connection_url: "sqlite:///opt/objectstore/keeper.db".into(),
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken default keeper database path

High Severity

The default connection_url points at /opt/objectstore/keeper.db, a path that does not exist elsewhere in this project. Local runs, Docker (/data), README env setup, and e2e tests all rely on this default when keeper is unset, so filesystem backend startup will fail creating the SQLite database.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 499ea21. Configure here.

_ => Err(Error::generic(format!("unknown backend {}", s))),
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeper backend config case mismatch

Medium Severity

KeeperBackend deserializes as Sqlite, but the filesystem config docs and FromStr accept sqlite. Config written as documented (or via env) will fail to parse. Other config enums in this repo use rename_all = "lowercase", and the new FromStr impl is never wired into serde.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 499ea21. Configure here.

{
objectstore_log::debug!("Object not found");
}
self.keeper.remove(id).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeper removed on failed deletes

Medium Severity

delete_object always calls keeper.remove before returning the filesystem delete result. If remove_file fails for a reason other than not found, the keeper entry is dropped while the object file remains, so TTL/TTI tracking is permanently lost for that object.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 499ea21. Configure here.

Comment thread objectstore-service/src/backend/local_fs.rs
Comment on lines +136 to +137
self.keeper.keep(id, metadata.expiration_policy).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Overwriting an object with a TTL/TTI policy fails due to a database UNIQUE constraint violation, causing data inconsistency despite the file being successfully updated.
Severity: HIGH

Suggested Fix

Modify the keeper.keep() method to handle cases where an object ID already exists. Instead of a simple INSERT, use an INSERT OR REPLACE or an equivalent UPSERT statement. This will ensure that overwriting an object also correctly updates its expiration policy in the keeper database without causing a constraint violation.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: objectstore-service/src/backend/local_fs.rs#L136-L137

Potential issue: When an object with a non-manual expiration policy (e.g., TTL or TTI)
is overwritten, the file is successfully updated on the filesystem, but the overall
operation fails. This occurs because the `keeper.keep()` method attempts to `INSERT` a
new record for the object's expiration into the SQLite database. Since a record for that
`object_id` already exists, this action violates the `PRIMARY KEY` constraint. The
resulting SQL error is propagated, causing the `put_object` request to fail, leading to
an inconsistency between the file storage and the expiration keeper database.

Also affects:

  • objectstore-service/src/backend/local_fs.rs:457~458

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit eba4ba5. Configure here.

file.sync_data().await?;
drop(file);

self.keeper.keep(id, metadata.expiration_policy).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overwrite breaks keeper retention tracking

High Severity

put_object and complete_multipart always call keeper.keep, but keep only inserts and rejects duplicates. Overwriting an object with a client-supplied key fails after the file is already replaced, or silently leaves a stale retention row when the new policy is Manual.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit eba4ba5. Configure here.

Comment on lines 195 to 199
{
objectstore_log::debug!("Object not found");
}
self.keeper.remove(id).await?;
Ok(result?)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: In delete_object, a failure in self.keeper.remove(id) can mask the result of the filesystem deletion, potentially causing the caller to believe an object exists when it has been deleted.
Severity: HIGH

Suggested Fix

Handle the result of the filesystem deletion before attempting the keeper database operation. For example, if the file is not found, return the NotFound error immediately. If the file is deleted successfully, proceed with the keeper operation but ensure its potential failure does not obscure the successful deletion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: objectstore-service/src/backend/local_fs.rs#L195-L199

Potential issue: In the `delete_object` function, the result of the filesystem deletion
is stored but not immediately returned. An unconditional call to
`self.keeper.remove(id).await?` follows. If this database operation fails (e.g., due to
an I/O error or the database being unavailable), the `?` operator will propagate the
database error, and the original result from the filesystem operation will be discarded.
This can lead to an inconsistent state where a file is successfully deleted from the
filesystem, but the caller receives a database error, leading it to believe the object
still exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant